class_name  Character
extends CharacterBody2D


enum EntryMode {
	## 默认，直接生成在占位符位置
	DEFAULT,
	## 生成在占位符指定的高度上，从高处落下
	DROP,
	## 从门里出来
	DOOR
}


@onready var animationPlayer: AnimationPlayer = $AnimationPlayer
# 角色精灵
@onready var characterSprite: Sprite2D = $CharacterSprite
# 实体碰撞
@onready var collisionShape: CollisionShape2D = $CollisionShape2D
# 伤害发射器
@onready var damageEmitter: Area2D = $DamageEmitter
# 伤害接收器
@onready var damageReceiver: DamageReceiver = $DamageReceiver
# 碰撞伤害触发器
@onready var collateralDamageEmitter: Area2D = $CollateralDamageEmitter
# 小刀精灵
@onready var knifeSprite: Sprite2D = $CharacterSprite/KnifeSprite
# 武器投掷时生成位置
@onready var weaponNode: Node2D = $CharacterSprite/KnifeSprite/WeaponNode
# 枪精灵
@onready var gunSprite: Sprite2D = $CharacterSprite/GunSprite
# 射线检测
@onready var projectileAim: RayCast2D = $ProjectileAim
# 可收集物检测
@onready var collectibleSensor: Area2D = $CollectibleSensor
@onready var stateMachine: NodeStateMachine = $StateMachine

## 角色是否可用，关闭后角色不进入任何状态
@export var enabled: bool = true
## 最大生命值
@export var max_health: int
## 普通攻击伤害
@export var damage: int
## 强力攻击伤害
@export var damage_power: int
## 射击伤害
@export var damage_shot: int
## 移动速度
@export var speed: float
## 受击飞出后的飞行速度
@export var flight_speed: float

## 重力
@export var gravity := GlobalConstants.GRAVITY

## 跳跃的强度，决定跳跃的高度
@export var jump_intensity: float

## 被击退的强度，决定了受击后退的距离
@export var knockback_intensity: float = 0.0
## 被击倒的强度，决定了受击后的高度
@export var knockdown_intensity: float = 0.0

@export_group("Weapons")
## 角色初始时是否持有小刀
@export var knife_enabled: bool = false
## 角色初始时是否持有枪
@export var gun_enabled: bool = false

## 掉落小刀或枪时，小刀或枪在落地后是否自动销毁
@export var auto_destroy_on_drop: bool = false

## 子弹最大数量
@export var max_bullet_number: int = 9999

## 死亡时发出
@warning_ignore("unused_signal")
signal death(character: Character)


# 生成后进入的方式
var spawn_entry_mode: Character.EntryMode = Character.EntryMode.DEFAULT
# 生成后落下的高度
var spawn_drop_height: float = 0.0
# 生成后经过指定的显现时间切换至 idle 状态
var spawn_appear_time: float = 0.0


## 当前生命值
var current_health: int
## 当前子弹数量
var current_bullet_number: int = 0

## 是否拥有小刀
var has_knife: bool:
	get:
		return knifeSprite.visible
	set(value):
		knifeSprite.visible = value
		knife_enabled = value

## 是否拥有枪
var has_gun: bool:
	get:
		return gunSprite.visible
	set(value):
		gunSprite.visible = value
		gun_enabled = value



func _ready():
	current_bullet_number = max_bullet_number
	knifeSprite.visible = knife_enabled
	gunSprite.visible = gun_enabled

	stateMachine.enabled = enabled

	# 初始生命值
	current_health = max_health


func flip_sprite():
	if velocity.x > 0:
		characterSprite.scale.x = 1.0
		projectileAim.scale.x = 1.0
	elif velocity.x < 0:
		characterSprite.scale.x = -1.0
		projectileAim.scale.x = -1.0


# 角色朝向，1：右，-1：左
func character_orientation() -> int:
	if characterSprite.scale.x < 0:
		return -1
	else:
		return 1


# 看向指定的节点
func look_at_node(node: Node2D):
	if node == null:
		return
	var diff := global_position.x - node.global_position.x
	if diff > 0:
		characterSprite.scale.x = -1.0
		projectileAim.scale.x = -1.0
	elif diff < 0:
		characterSprite.scale.x = 1.0
		projectileAim.scale.x = 1.0


func look_at_direction(direction: Vector2):
	if direction.x > 0:
		characterSprite.scale.x = 1.0
		projectileAim.scale.x = 1.0
	elif direction.x < 0:
		characterSprite.scale.x = -1.0
		projectileAim.scale.x = -1.0


func _can_pickup_weapon() -> bool:
	return not has_knife and not has_gun

func _can_pickup_food() -> bool:
	return true


# 尝试收集，没有可收集物是返回null
func try_pickup_collectible() -> Collectible:
	var collectible_areas = collectibleSensor.get_overlapping_areas()
	if collectible_areas.size() == 0:
		return null
	var index := collectible_areas.find_custom(
		func(it):
			return it as Collectible != null
	)
	if index < 0:
		return null
	var collectible := collectible_areas[index] as Collectible
	match collectible.type:
		Collectible.Type.KNIFE, Collectible.Type.GUN:
			# 当已经有小刀、枪或者能够重生小刀时不能再拾取
			if _can_pickup_weapon():
				return collectible
			return null
		Collectible.Type.FOOD:
			return collectible
		_:
			return null


# 拾取收集物
func pickup_collectible(collectible: Collectible):
	if collectible == null:
		return
	match collectible.type:
		Collectible.Type.KNIFE:
			has_knife = true
		Collectible.Type.GUN:
			has_gun = true
			current_bullet_number = max_bullet_number
		Collectible.Type.FOOD:
			var food := collectible as Food
			set_current_health(current_health + food.health)
			SoundPlayer.play(SoundManager.Sound.EAT_FOOD)
		_:
			pass
	collectible.queue_free()


# 开枪
func shoot_gun():
	var target_x: float

	var target := projectileAim.get_collider()
	if target == null:
		target_x = character_orientation() * (global_position.x + get_viewport_rect().size.x)
	else:
		target_x = projectileAim.get_collision_point().x

	var gun_position := Vector2(weaponNode.global_position.x, position.y)
	var distance_traveled := target_x - weaponNode.global_position.x
	EntityManager.spawn_shot.emit(gun_position, distance_traveled, -weaponNode.position.y)
	SoundPlayer.play(SoundManager.Sound.GUNSHOT)

	# 若目标存在 on_receive_damage 方法，则调用向其发送伤害
	if target and target.has_method("on_receive_damage"):
		target.call_deferred("on_receive_damage",
							damage_shot,
							target.global_position - global_position,
							DamageReceiver.HitType.SHOOT)


# 投掷武器
func throw_weapon(type: Collectible.Type):
	var weapon_position := Vector2(weaponNode.global_position.x, global_position.y)
	var weapon_direction := Vector2(character_orientation(), 0.0)
	var weapon_height := absf(weaponNode.position.y)
	EntityManager.spawn_collectible.emit(type,
										Collectible.STATE_FLY,
										weapon_position,
										weapon_direction,
										weapon_height,
										true)
	SoundPlayer.play(SoundManager.Sound.SWOOSH)



# 丢失刀具，需要让小刀掉落到地面
func lost_knife():
	if not has_knife:
		return
	has_knife = false
	EntityManager.spawn_collectible.emit(Collectible.Type.KNIFE,
										Knife.STATE_FALL,
										global_position,
										character_orientation() * Vector2.RIGHT,
										0.0,
										auto_destroy_on_drop)


# 丢失枪
func lost_gun():
	if not has_gun:
		return
	has_gun = false
	EntityManager.spawn_collectible.emit(Collectible.Type.GUN,
										Knife.STATE_FALL,
										global_position,
										character_orientation() * Vector2.RIGHT,
										0.0,
										auto_destroy_on_drop)



func is_bullet_empty() -> bool:
	return current_bullet_number == 0


# 设置当前生命值
func set_current_health(health: int):
	current_health = clamp(health, 0, max_health)


# 设置检测是否关闭
func set_collision_disabled(disabled: bool):
	collisionShape.set_deferred("disabled", disabled)
	damageEmitter.set_deferred("monitoring", not disabled)
	damageReceiver.set_deferred("monitorable", not disabled)
	collectibleSensor.set_deferred("monitoring", not disabled)
	projectileAim.set_deferred("enabled", not disabled)
